package com.qying.rpc.provider;

import com.qying.rpc.codec.MiniRpcDecoder;
import com.qying.rpc.codec.MiniRpcEncoder;
import com.qying.rpc.common.RpcServiceHelper;
import com.qying.rpc.common.ServiceMeta;
import com.qying.rpc.handler.RpcRequestHandler;
import com.qying.rpc.provider.annotation.RpcService;
import com.qying.rpc.registry.RegistryService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class RpcProvider implements InitializingBean, BeanPostProcessor {


    /**
     * 地址
     */
    private String serviceAddress;

    /**
     * 服务端口
     */
    private final Integer servicePort;

    /**
     * 注册机信息
     */
    private final RegistryService registryService;

    private final Map<String, Object> rpcServiceMap = new HashMap<>();


    public RpcProvider(int servicePort, RegistryService registryService) {
        this.servicePort = servicePort;
        this.registryService = registryService;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        new Thread(()->{
            try {
                startRpcService();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

    }

    private void startRpcService() throws Exception{

        this.serviceAddress = InetAddress.getLocalHost().getHostAddress();

        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();

            bootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new MiniRpcEncoder())
                                    .addLast(new MiniRpcDecoder())
                                    .addLast(new RpcRequestHandler(rpcServiceMap));
                        }
                    }).childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture channelFuture = bootstrap.bind(this.serviceAddress, this.servicePort).sync();
            log.info("server addr {} started on port {}", this.serviceAddress, this.servicePort);

            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }



    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);

        if(Objects.nonNull(rpcService)) {
            String serviceName = rpcService.serviceInterface().getName();
            String serviceVersion = rpcService.serviceVersion();

            try {
                ServiceMeta serviceMeta = new ServiceMeta();

                serviceMeta.setServiceAddr(serviceAddress);
                serviceMeta.setServicePort(servicePort);
                serviceMeta.setServiceName(serviceName);
                serviceMeta.setServiceVersion(serviceVersion);

                registryService.register(serviceMeta);
                rpcServiceMap.put(RpcServiceHelper.buildServiceKey(serviceName,serviceVersion), bean);
            } catch (Exception e) {
                log.error("registry fail");
            }
        }

        return  bean;
    }
}
